import { Decimal } from 'decimal.js';

// 核心计算器封装
const useCalculator = () => {
	// 配置20位计算精度
	Decimal.set({ precision: 20 });

	// 运算符优先级配置（包含负号运算符）
	const precedence = {
		negate: 4,
		'+': 1,
		'-': 1,
		'*': 2,
		'/': 2,
		'^': 3,
		'%': 2
	};

	// 主计算函数
	const calculate = (expression) => {
		try {
			const tokens = tokenize(expression);
			const rpn = shuntingYard(tokens);
			return evaluateRPN(rpn);
		} catch (error) {
			return { error: error.message };
		}
	};

	// 改进的词法分析器（支持负数）
	const tokenize = (expr) => {
		const tokenRegex = /(-?\d+\.?\d*|\.\d+|[-+*/^%()]|sqrt|pi|π)/g;
		const tokens = [];
		let match;
		let prevToken = null;

		while ((match = tokenRegex.exec(expr)) !== null) {
			let [token] = match;

			// 处理负号作为一元运算符的情况
			if (token === '-' && (!prevToken || prevToken.type === 'operator' || prevToken.value === '(')) {
				tokens.push({ type: 'operator', value: 'negate' });
			} else if (/\d/.test(token)) {
				tokens.push({ type: 'number', value: new Decimal(token) });
			} else if (['sqrt', 'pi', 'π'].includes(token)) {
				tokens.push({ type: 'function', value: token.toLowerCase() });
			} else {
				tokens.push({ type: 'operator', value: token });
			}

			prevToken = tokens[tokens.length - 1];
		}
		return tokens;
	};

	// Shunting Yard 算法转换RPN（支持一元运算符）
	const shuntingYard = (tokens) => {
		const output = [];
		const stack = [];

		tokens.forEach((token) => {
			if (token.type === 'number') {
				output.push(token.value);
			} else if (token.type === 'function') {
				stack.push(token);
			} else if (token.value === '(') {
				stack.push(token);
			} else if (token.value === ')') {
				while (stack.length && stack[stack.length - 1].value !== '(') {
					output.push(stack.pop().value);
				}
				stack.pop();
				if (stack.length && stack[stack.length - 1].type === 'function') {
					output.push(stack.pop().value);
				}
			} else {
				while (stack.length && stack[stack.length - 1].value !== '(' && precedence[stack[stack.length - 1].value] >= precedence[token.value]) {
					output.push(stack.pop().value);
				}
				stack.push(token);
			}
		});

		return [...output, ...stack.reverse().map((t) => t.value)];
	};

	// RPN 求值（支持一元运算符）
	const evaluateRPN = (rpn) => {
		const stack = [];

		rpn.forEach((token) => {
			if (token instanceof Decimal) {
				stack.push(token);
			} else if (typeof token === 'string') {
				handleOperation(stack, token);
			}
		});

		if (stack.length !== 1) throw new Error('无效表达式');
		return parseFloat(stack.pop().toDecimalPlaces(10).toNumber());
	};

	// 运算符处理（包含负号运算）
	const handleOperation = (stack, operator) => {
		const operations = {
			'+': (a, b) => a.add(b),
			'-': (a, b) => a.sub(b),
			'*': (a, b) => a.mul(b),
			'/': (a, b) => a.div(b),
			'^': (a, b) => a.pow(b),
			'%': (a, b) => a.mod(b),
			sqrt: (a) => a.sqrt(),
			negate: (a) => a.neg(),
			pi: () => new Decimal(Decimal.acos(-1))
		};

		if (operator === 'negate') {
			if (stack.length < 1) throw new Error('缺少操作数');
			const a = stack.pop();
			stack.push(operations[operator](a));
		} else if (operator === 'pi' || operator === 'π') {
			stack.push(operations['pi']());
		} else if (operator === 'sqrt') {
			if (stack.length < 1) throw new Error('缺少操作数');
			const a = stack.pop();
			stack.push(operations[operator](a));
		} else {
			if (stack.length < 2) throw new Error('操作数不足');
			const b = stack.pop();
			const a = stack.pop();
			stack.push(operations[operator](a, b));
		}
	};

	return {
		calculate
	};
};

const { calculate } = useCalculator();
export function calculator(expression) {
	return calculate(expression);
}

/** 加法（支持多参数） */
export const add = (...nums) => {
	return nums.reduce((acc, num) => new Decimal(acc).add(new Decimal(num))).toNumber();
};

/** 减法（支持多参数链式计算） */
export const sub = (...nums) => {
	return nums.reduce((acc, num) => new Decimal(acc).sub(new Decimal(num))).toNumber();
};

/** 乘法（支持多参数） */
export const mul = (...nums) => {
	return nums.reduce((acc, num) => new Decimal(acc).mul(new Decimal(num))).toNumber();
};

/** 除法（包含除零校验） */
export const div = (a, b) => {
	if (Number(b) === 0) {
		return NaN;
	}
	if (new Decimal(b).equals(0)) throw new Error('除数不能为零');
	return new Decimal(a).div(new Decimal(b)).toNumber();
};
